# Copyright 2021, Robert Adam. All rights reserved.
# Use of this source code is governed by a BSD-style license
# that can be found in the LICENSE file at the root of the
# source tree.
# from https://github.com/Krzmbrzl/FindPythonInterpreter/blob/main/FindPythonInterpreter.cmake

cmake_minimum_required(VERSION 3.20)

function(find_python_interpreter)
    set(options REQUIRED EXACT)
    set(oneValueArgs VERSION INTERPRETER_OUT_VAR VERSION_OUT_VAR)
    set(multiValueArgs HINTS)
    cmake_parse_arguments(FIND_PYTHON_INTERPRETER "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})


    # Error handling
    if (FIND_PYTHON_INTERPRETER_UNPARSED_ARGUMENTS)
        message(FATAL_ERROR "Unrecognized arguments to find_python_interpreter: \"${FIND_PYTHON_INTERPRETER_UNPARSED_ARGUMENTS}\"")
    endif()
    if (NOT FIND_PYTHON_INTERPRETER_INTERPRETER_OUT_VAR)
        message(FATAL_ERROR "Called find_python_interpreter without the INTERPRETER_OUT_VAR parameter!")
    endif()
    if (FIND_PYTHON_INTERPRETER_EXACT AND NOT FIND_PYTHON_INTERPRETER_VERSION)
        message(FATAL_ERROR "Specified EXACT but did not specify VERSION!")
    endif()


    # Defaults
    if (NOT FIND_PYTHON_INTERPRETER_VERSION)
        set(FIND_PYTHON_INTERPRETER_VERSION "0.0.0")
    endif()
    if (NOT FIND_PYTHON_INTERPRETER_HINTS)
        set(FIND_PYTHON_INTERPRETER_HINTS "")
    endif()


    # Validate
    if (NOT FIND_PYTHON_INTERPRETER_VERSION MATCHES "^[0-9]+(\.[0-9]+(\.[0-9]+)?)?$")
        message(FATAL_ERROR "Invalid VERSION \"FIND_PYTHON_INTERPRETER_VERSION\" - must follow RegEx \"^[0-9]+(\.[0-9]+(\.[0-9]+)?)?$\"")
    endif()


    # "parse" version (first append 0.0.0 in case only a part of the version scheme was set by the user)
    string(CONCAT VERSION_HELPER "${FIND_PYTHON_INTERPRETER_VERSION}" ".0.0.0")
    string(REPLACE "." ";" VERSION_LIST "${VERSION_HELPER}")
    list(GET VERSION_LIST 0 FIND_PYTHON_INTERPRETER_VERSION_MAJOR)
    list(GET VERSION_LIST 1 FIND_PYTHON_INTERPRETER_VERSION_MINOR)
    list(GET VERSION_LIST 1 FIND_PYTHON_INTERPRETER_VERSION_PATCH)


    # Create names for the interpreter to search for
    set(INTERPRETER_NAMES "")
    if (FIND_PYTHON_INTERPRETER_VERSION_MAJOR STREQUAL "0")
        # Search for either Python 2 or 3
        list(APPEND INTERPRETER_NAMES "python3")
        list(APPEND INTERPRETER_NAMES "python")
        list(APPEND INTERPRETER_NAMES "python2")
    else()
        # Search for specified version
        list(APPEND INTERPRETER_NAMES "python${FIND_PYTHON_INTERPRETER_VERSION_MAJOR}")
        list(APPEND INTERPRETER_NAMES "python")

        if (NOT FIND_PYTHON_INTERPRETER_VERSION_MINOR EQUAL 0)
            list(PREPEND INTERPRETER_NAMES "python${FIND_PYTHON_INTERPRETER_VERSION_MAJOR}.${FIND_PYTHON_INTERPRETER_VERSION_MINOR}")

            if (NOT FIND_PYTHON_INTERPRETER_VERSION_PATCH EQUAL 0)
                list(PREPEND INTERPRETER_NAMES
                        "python${FIND_PYTHON_INTERPRETER_VERSION_MAJOR}.${FIND_PYTHON_INTERPRETER_VERSION_MINOR}.${FIND_PYTHON_INTERPRETER_VERSION_PATCH}")
            endif()
        endif()
    endif()


    # Start by trying to search for a python executable in PATH and HINTS
    find_program(PYTHON_INTERPRETER NAMES ${INTERPRETER_NAMES} HINTS ${FIND_PYTHON_INTERPRETER_HINTS})


    if (NOT PYTHON_INTERPRETER)
        # Fall back to find_package
        message(VERBOSE "Can't find Python interpreter in PATH -> Falling back to find_package")
        if (FIND_PYTHON_INTERPRETER_VERSION_MAJOR EQUAL 0)
            # Search arbitrary version
            find_package(Python COMPONENTS Interpreter QUIET)
            set(PYTHON_INTERPRETER "${Python_EXECUTABLE}")
        else()
            # Search specific version (Python 2 or 3)
            find_package(Python${FIND_PYTHON_INTERPRETER_VERSION_MAJOR} COMPONENTS Interpreter QUIET)
            set(PYTHON_INTERPRETER "${Python${FIND_PYTHON_INTERPRETER_VERSION_MAJOR}_EXECUTABLE}")
        endif()
    endif()


    if (PYTHON_INTERPRETER)
        # Verify that the version found is the one that is wanted
        execute_process(
                COMMAND ${PYTHON_INTERPRETER} "--version"
                OUTPUT_VARIABLE PYTHON_INTERPRETER_VERSION
                ERROR_VARIABLE PYTHON_INTERPRETER_VERSION # Python 2 reports the version on stderr
        )

        # Remove leading "Python " from version information
        string(REPLACE "Python " "" PYTHON_INTERPRETER_VERSION "${PYTHON_INTERPRETER_VERSION}")
        string(STRIP "${PYTHON_INTERPRETER_VERSION}" PYTHON_INTERPRETER_VERSION)


        if (PYTHON_INTERPRETER_VERSION VERSION_LESS FIND_PYTHON_INTERPRETER_VERSION)
            message(STATUS "Found Python version ${PYTHON_INTERPRETER_VERSION} but required at least ${FIND_PYTHON_INTERPRETER_VERSION}")
            set(PYTHON_INTERPRETER "NOTFOUND")
            set(PYTHON_INTERPRETER_VERSION "NOTFOUND")
        elseif(PYTHON_INTERPRETER_VERSION VERSION_GREATER FIND_PYTHON_INTERPRETER_VERSION AND FIND_PYTHON_INTERPRETER_EXACT)
            message(STATUS "Found Python interpreter version ${PYTHON_INTERPRETER_VERSION} but required exactly ${FIND_PYTHON_INTERPRETER_VERSION}")
            set(PYTHON_INTERPRETER "NOTFOUND")
            set(PYTHON_INTERPRETER_VERSION "NOTFOUND")
        else()
            message(STATUS "Found Python interpreter version ${PYTHON_INTERPRETER_VERSION}")
        endif()
    else()
        set(PYTHON_INTERPRETER_VERSION "NOTFOUND")
    endif()


    # Set "return" values
    set(${FIND_PYTHON_INTERPRETER_INTERPRETER_OUT_VAR} "${PYTHON_INTERPRETER}" PARENT_SCOPE)
    if (FIND_PYTHON_INTERPRETER_VERSION_OUT_VAR)
        set(${FIND_PYTHON_INTERPRETER_VERSION_OUT_VAR} "${PYTHON_INTERPRETER_VERSION}" PARENT_SCOPE)
    endif()

    if (NOT PYTHON_INTERPRETER AND FIND_PYTHON_INTERPRETER_REQUIRED)
        message(FATAL_ERROR "Did NOT find Python interpreter")
    endif()
endfunction()
